package com.doliere.android.app.wifidirectservice.activities;

import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import com.doliere.android.app.wifidirectservice.listeners.BReceiver;

import android.net.wifi.p2p.WifiP2pConfig;
import android.net.wifi.p2p.WifiP2pDevice;
import android.net.wifi.p2p.WifiP2pInfo;
import android.net.wifi.p2p.WifiP2pManager;
import android.net.wifi.p2p.WifiP2pManager.Channel;
import android.net.wifi.p2p.WifiP2pManager.DnsSdServiceResponseListener;
import android.net.wifi.p2p.WifiP2pManager.DnsSdTxtRecordListener;
import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceInfo;
import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceRequest;
import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
import android.os.Bundle;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.IntentFilter;
import android.view.Menu;
import android.widget.Toast;

public class MainActivity extends Activity {
	
	private WifiP2pManager mWifiP2pManager; // Wifi Direct manager.
	private Channel channel;  // Channel that enabled Wifi Direct callback methods.
	BroadcastReceiver mReceiver; // Broadcast Receiver that 
	IntentFilter mIntentFilter;
	
	private WifiP2pDnsSdServiceInfo mDnsServiceInfo;
	private WifiP2pDnsSdServiceRequest mDnsServiceRequest;
	private WifiP2pServiceInfo mWifiP2pServiceInfo;
	
	//
	private DnsSdTxtRecordListener mDnsTextRecordListener;
	private DnsSdServiceResponseListener mDnsServiceResponseListener;
	
	private Map<String, String> mMapServiceInfo;
	private HashMap<String, String> mMapOfServices;
	private HashMap<String, String> mAllServices;
	private HashMap<String, String> mSismappServices;
	private ArrayList<WifiP2pDevice> mSismappDevices;
	
	private final String SERVICE_NAME = "SERVICE_NAME";
	private final String mServiceName = "SISMAPP";
	private final String SERVICE_TYPE = "SERVICE_TYPE";
	private final String mServiceType = "_http._tcp.";
	private final String INSTANCE_NAME = "SISMAPP_P2P_SERVICE_DISCOVERY";
	private final String REGISTRATION_TYPE = "_presence._tcp.";
	private final String SERVICE_PORT = "SERVICE_LISTEN_PORT";
	
	private ServerSocket mServerSocket;
	private int mLocalPort;
	
	private boolean mserviceRegistered = false;
	private boolean mServiceRequestReady = false;
	private boolean mServiceDiscovery = false;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		init(); // Init the P2P wifi and service features
	}
	
	private void init(){
		//Initialize the P2P functionnalities.
		mWifiP2pManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE); // Get a reference to the P2P service
		channel = mWifiP2pManager.initialize(this, getMainLooper(), null); // Initialize a channel that enables use of P2P callback methods.
		mReceiver = new BReceiver(mWifiP2pManager, channel, this);
		//Adding and intent filter to receive intents relative to P2P communication
		mIntentFilter = new IntentFilter(); 
		mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
		mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
		mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
		mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
		mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION);
		
		//Initialize the P2P service functionnalities.
		initializeServerSocket(); // Initialize a server socket and get a free port.
		initializeServiceInfo(); // Add the service to the local services.
		
		//Initialize the service listeners.
		initializeTextRecordListener();
		initializeDnsServiceResponseListener();
		
		setDnsListeners(); // Set up the 2 DNS listeners bellow.
		
		initializeServiceRequest(); //
		startServiceDiscovery(); // Start service discoveries
	}
	//Register the Broadcast receiver when the activity goes to the front.
	@Override
	protected void onResume(){
		super.onResume();
		registerReceiver(mReceiver, mIntentFilter);
	}
	
	//Unregister the Braodcast receiver when the activity pauses.
	@Override
	protected void onPause(){
		super.onPause();
		unregisterReceiver(mReceiver);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}
	
	/**
	 * @author doliere
	 * Initialize a serverSocket and get a free port on which the server will be published.
	 */
	private void initializeServerSocket(){
		try {
			mServerSocket = new ServerSocket(0);
			mLocalPort = mServerSocket.getLocalPort();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			mLocalPort = -1;
		}
	}
	
	
	/**
	 * @author doliere
	 * The map record is published with the service name, type and port on which the service is displayed.
	 * All the services implementing this application, has the same INSTANCE_NAME with a random number concatened to it.
	 * The registration type is the same with this application.
	 */
	private void initializeServiceInfo(){
		
		//Initialize a record containing the service informations : name, and type, and others...
		mMapServiceInfo = new HashMap<String, String>();
		mMapServiceInfo.put(SERVICE_NAME, mServiceName);
		mMapServiceInfo.put(SERVICE_TYPE, mServiceType);
		mMapServiceInfo.put(SERVICE_PORT, String.valueOf(mLocalPort));
		
		//Initialize the a Dsn Service info that will be published on the peer to peer networks, according to a protocol.
		mDnsServiceInfo = WifiP2pDnsSdServiceInfo.newInstance(INSTANCE_NAME + (int)(Math.random() * 1000), REGISTRATION_TYPE, mMapServiceInfo);
		
		//Register the service to the peer to peer services.
		mWifiP2pManager.addLocalService(channel, mDnsServiceInfo, new WifiP2pManager.ActionListener() {
			
			@Override
			public void onSuccess() {
				mserviceRegistered = true;
				toaster("SISMAPP service added to local services");
			}
			
			@Override
			public void onFailure(int reason) {
				mserviceRegistered = false;
				toaster(getReason(reason));
			}
		});
	}
	
	
	/**
	 * @author doliere
	 * Initialize a text record listener, which onDnsSdTxtRecordAvaialbe method is called to notify the application
	 * about every peer to peer service available, which instance name and registration port are not available.
	 */
	private void initializeTextRecordListener(){
		mAllServices = new HashMap<String, String>();
		mDnsTextRecordListener = new DnsSdTxtRecordListener() {
			
			@Override
			public void onDnsSdTxtRecordAvailable(String fulldomain, Map<String, String> record, WifiP2pDevice device) {
				toaster("Service available");
				mAllServices.put(fulldomain, device.deviceAddress);
			}
		};
	}
	
	/**
	 * @author doliere
	 * This method is called when the a peer to peer service, which instance and registration type are available.
	 * For this application, we just keep devices that instance name starts with SISMAPP and which registration type 
	 * is _http._tcp.
	 */
	private void initializeDnsServiceResponseListener(){
		mDnsServiceResponseListener = new DnsSdServiceResponseListener() {
			
			@Override
			public void onDnsSdServiceAvailable(String instanceName,
					String registrationType, WifiP2pDevice srcDevice) {
				toaster("Dns Service Available");
				
				
				if(instanceName.startsWith(instanceName) && registrationType.equals(registrationType)){
					mSismappServices.put(instanceName, srcDevice.deviceAddress);
					mSismappDevices.add(srcDevice);
				}
				
			}
		};
	}
	
	//Try to establish a connexion to a peer
	private void connectToAPeer(WifiP2pDevice device){
		WifiP2pConfig config = new WifiP2pConfig();
		config.deviceAddress = device.deviceAddress;
		mWifiP2pManager.connect(channel, config, new WifiP2pManager.ActionListener() {
			
			@Override
			public void onSuccess() {
				toaster("Connected to a peer");
				requestConnectionInformations();
			}
			
			@Override
			public void onFailure(int reason) {
				toaster(getReason(reason));
			}
		});
	}
	
	//Request connexion informations, including ip adress, for socket communications.
	private void requestConnectionInformations(){
		mWifiP2pManager.requestConnectionInfo(channel, new WifiP2pManager.ConnectionInfoListener() {
			
			@Override
			public void onConnectionInfoAvailable(WifiP2pInfo info) {
				// TODO Auto-generated method stub
				InetAddress adr = info.groupOwnerAddress; // How to get an ip address and set up a socket connexion
			}
		});
	}
	
	private void setDnsListeners(){
		mWifiP2pManager.setDnsSdResponseListeners(channel, mDnsServiceResponseListener, mDnsTextRecordListener);
	}
	
	/**
	 * @author doliere
	 */
	
	private void initializeServiceRequest(){
		mDnsServiceRequest = WifiP2pDnsSdServiceRequest.newInstance();
		mWifiP2pManager.addServiceRequest(channel, mDnsServiceRequest, new WifiP2pManager.ActionListener() {
			
			@Override
			public void onSuccess() {
				// TODO Auto-generated method stub
				toaster("Application ready to receive P2p service request");
				mServiceRequestReady = true;
			}
			
			@Override
			public void onFailure(int reason) {
				mServiceRequestReady = false;
				toaster(getReason(reason));
			}
		});
	}
	
	//Register the application for service discovery.
	private void startServiceDiscovery(){
		mWifiP2pManager.discoverServices(channel, new WifiP2pManager.ActionListener() {
			
			@Override
			public void onSuccess() {
				mServiceDiscovery = true;
				toaster("Service Discovery started");
			}
			
			@Override
			public void onFailure(int reason) {
				mServiceDiscovery = false;
				toaster(getReason(reason));
			}
		});
	}
	/**
	 * @author doliere
	 * @param reason
	 * @return {@link String}
	 * <p>Take an integer and returns a string describing an WifiP2p ActionListener reason.</p>
	 */
	public String getReason(int reason){
		String reas = "";
		switch(reason){
			case WifiP2pManager.P2P_UNSUPPORTED :
				reas = "P2P unsupported by this device";
				break;
			case WifiP2pManager.ERROR :
				reas = "ERROR : An internal error occured !";
				break;
			case WifiP2pManager.BUSY :
				reas = "WifiP2pManager framework Busy : try later";
				break;
			default :
				reas = "Unknown Failure Reason";
				break;
		}
		return reas;
	}
	
	public void toaster(String texte){
		Toast.makeText(getApplicationContext(), texte, Toast.LENGTH_SHORT).show();
	}

}
